import json
import lxml.etree
import web

import model


def clear_text(text):
    """Fix windows line feeds, otherwise there'll be &#13; in the response."""
    return text.replace("\r\n", "\n")


def format_songs_xml(songs):
    _response = lxml.etree.Element("response")

    for song in songs:
        _song = lxml.etree.SubElement(_response, "song")
        _song.set("id", str(song["query_id"]))

        _text = lxml.etree.SubElement(_song, "text")
        _text.set("id", str(song["id"]))
        _text.text = clear_text(song["text"])

    return lxml.etree.tostring(_response, encoding="UTF-8", xml_declaration=True)


def format_update_response_xml(stats_added, stats_was):
    _response = lxml.etree.Element("response")
    _update = lxml.etree.SubElement(_response, "update")
    _statistics = lxml.etree.SubElement(_update, "statistics")
    _statistics.set("added", str(stats_added))
    _statistics.set("was", str(stats_was))
    _statistics.set("error", str(0))

    return lxml.etree.tostring(_response, encoding="UTF-8", xml_declaration=True)


class ServerController:
    def GET(self):
        return "POST please.  API docs: http://lyrics.mirkforce.net/lep.html"

    def POST(self):
        xml = web.data()
        query = lxml.etree.fromstring(xml)

        agent_name = query.get("agent")
        agent_version = query.get("version")

        gets = []
        updates = None

        for em in query:
            if em.tag == "update":
                updates = em
            else:
                gets.append(em)

        if updates is not None:
            user = model.User.find_by_login_and_password(updates.get("login"), updates.get("password"))
            return self.process_updates(agent_name, agent_version, user, updates)
        else:
            return self.process_gets(agent_name, agent_version, gets)

    def process_updates(self, agent_name, agent_version, user, songs):
        stats_total, stats_was = 0, 0

        for song in songs:
            artist = song.get("artist")
            album = song.get("album")
            title = song.get("title")
            text = song[0].text

            old_count = self.add_song(user, artist, album, title, text)

            stats_total += 1
            if old_count:
                stats_was += 1

            self.log_request(agent_name, agent_version, "post", artist, album, title, None)

        model.commit()

        return format_update_response_xml(stats_total, stats_was)

    def process_gets(self, agent_name, agent_version, songs):
        output = []

        for song in songs:
            artist = song.get("artist")
            title = song.get("title")
            album = None
            lyric = self.find_song(agent_name, agent_version, artist, title)

            if lyric is not None:
                lyric["query_id"] = song.get("id")
                output.append(lyric)
                album = lyric["album"]

            self.log_request(agent_name, agent_version, "req", artist, album, title, lyric is not None)

        model.commit()

        web.header("Content-Type", "text/xml; charset=UTF-8")
        return format_songs_xml(output)

    def find_song(self, agent_name, agent_version, artist, title):
        song = model.Lyric.find_by_artist_and_title(artist, title)
        if song is not None:
            return {"id": song["LyID"], "album": song["album"], "text": song["lyrics"]}
        return None

    def add_song(self, user, artist, album, title, text, uid=None):
        artist = model.Author.find_by_name(artist, create=True)

        song = model.Lyric(title=title, album=album, text=text)
        song.set_user(user)
        song.set_artist(artist)
        song.put()

        old_count = song.freeze_old()
        return old_count

    def log_request(self, agent_name, agent_version, action, artist, album, title, success):
        cst = model.ClientStat()
        cst["agent"] = agent_name
        cst["aver"] = agent_version
        cst["author"] = artist
        cst["album"] = album
        cst["title"] = title
        cst["got"] = int(success)
        cst["addr"] = web.ctx.environ["REMOTE_ADDR"]
        cst["action"] = action
        cst.put()


class ExportController:
    def GET(self, format):
        args = web.input(id=None, count=100)
        if args.id is None:
            raise web.notfound("Forgot to specify id to start with.")

        rows = model.Lyric.find_since(int(args.id), int(args.count))
        return self.send_json(rows)

    def send_json(self, songs):
        output = []

        for song in songs:
            artist = [song.get_artist_name()]
            artist += song.get_artist_aliases()

            output.append({
                "id": song["LyID"],
                "artist": artist,
                "title": song["title"],
                "text": song["lyrics"],
            })

        return json.dumps(output, indent=True)


def transaction_fix(handler):
    model.rollback()
    return handler()


def get_application():
    app = web.application((
        "/cgi-bin/lepserver\.cgi", ServerController,
        "/api/export\.(json|xml)", ExportController,
    ), globals())
    app.add_processor(transaction_fix)
    return app


def serve_http():
    get_application().run()


__all__ = ["serve_http"]
